package com.freewater.javabase.demo.mutithread.virtualthread;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * <p>
 * You often want to combine the results of multiple concurrent tasks:
 * </p>
 * <p>
 * Future<T1> f1 = service.submit(callable1);
 * Future<T2> f2 = service.submit(callable2);
 * result = combine(f1.get(), f2.get());
 * </p>
 * <p>
 * Before virtual threads, you might have felt bad about the blocking get calls. But now blocking is cheap.
 * But now blocking is cheap. Here is a sample program with a more concrete example:
 * </p>
 */
public class CapturingTaskResultsDemo {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        demo1();
        demo2();
    }

    private static void demo1() throws InterruptedException, ExecutionException {
        ExecutorService service = Executors.newVirtualThreadPerTaskExecutor();
        Future<String> f1 = service.submit(() -> get("https://horstmann.com/random/adjective"));
        Future<String> f2 = service.submit(() -> get("https://horstmann.com/random/noun"));
        String result = f1.get() + " " + f2.get();
        System.out.println(result);
        service.close();
    }

    public static void demo2() throws InterruptedException, ExecutionException {
        ExecutorService service = Executors.newVirtualThreadPerTaskExecutor();
        List<Callable<String>> callables = new ArrayList<>();
        final int ADJECTIVES = 4;
        for (int i = 1; i <= ADJECTIVES; i++) {
            callables.add(() -> get("https://horstmann.com/random/adjective"));
        }
        callables.add(() -> get("https://horstmann.com/random/noun"));
        List<String> results = new ArrayList<>();
        for (Future<String> f : service.invokeAll(callables)) {
            results.add(f.get());
        }
        System.out.println(String.join(" ", results));
        service.close();
    }

    private static final HttpClient client = HttpClient.newHttpClient();

    public static String get(String url) {
        try {
            var request = HttpRequest.newBuilder().uri(new URI(url)).GET().build();
            return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
        } catch (Exception ex) {
            var rex = new RuntimeException();
            rex.initCause(ex);
            throw rex;
        }
    }
}
